VS2019+PCL+LAS点云学习

2023/10/12 10:52:02

VS2019+PCL+LAS点云学习教程、基本代码

部分教程汇总

安装教程
  1. win10系统下 VS2019点云库PCL1.12.0的安装与配置:https://blog.csdn.net/Nelson_Leo/article/details/118600787
  2. PCL点云库安装及学习(2021.7.28):https://blog.csdn.net/jing_zhong/article/details/118696089
  3. PCL安装教程(Win10+vs2019+PCL1.10.1):https://blog.csdn.net/weixin_43403569/article/details/118568577
  4. VS2019+pcl1.12.1配置详解:https://blog.csdn.net/biubiubiu011/article/details/127160201
  5. Gitee上传、更新仓库代码:https://blog.csdn.net/qq_38697586/article/details/128185549
其他教程
  1. pcl生成三个点连线,画箭头,画圆,改线条颜色:https://blog.csdn.net/qq_41092406/article/details/114041229

  2. 计算空间中点到直线的距离:https://blog.csdn.net/qq_37220275/article/details/117954824

  3. 三维空间的直线方程:https://blog.csdn.net/qq_39706570/article/details/105962373

  4. 已经两点求直线方程(多维空间):https://www.cnblogs.com/yaolin1228/p/7871793.html

  5. PCL生成线段点云:https://blog.csdn.net/Dbojuedzw/article/details/126127391

  6. 【计算几何】多边形点集排序:https://www.cnblogs.com/dwdxdy/p/3230156.html

  7. 空间点到直线垂足坐标的解算方法:https://blog.csdn.net/zhouschina/article/details/14647587

  8. pcl::compute3DCentroid()计算质心算法原理:

    https://blog.csdn.net/YunLaowang/article/details/102703768

    https://blog.csdn.net/u014072827/article/details/110746985

  9. PCL:点云赋色 | 自定义点云中任意一点的颜色:http://www.taodudu.cc/news/show-3123035.html

  10. PCL C++版【学习备忘】:https://blog.csdn.net/expert_joe/article/details/123342098

点云入门

功能示例代码段

1. pcl::PointCloud数据类型

  • 点云类型
// 点云指针类型
pcl::PointCloud<pcl::PointXYZ>::Ptr cloudPtr (new pcl::PointCloud<pcl::PointXYZ>);
// 点云对象
pcl::PointCloud<pcl::PointXYZ> cloud;
// 单个点云的点
pcl::PointXYZ  point;
  • 访问形式
// 访问第一个点的x
cloud.points[0].x;
cloudPtr->points[0].x;
// 访问单个点的x
point.x;

2. 从点云里删除点和添加点

  • 点云增删改查
#include <pcl/io/pcd_io.h>
#include <pcl/common/impl/io.hpp>
#include <pcl/point_types.h>
#include <pcl/point_cloud.h>

int main(int argc, char **argv)
{
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);
    pcl::io::loadPCDFile<pcl::PointXYZ>("bun0.pcd", *cloud);
    pcl::PointCloud<pcl::PointXYZ>::iterator index = cloud->begin();
    // 删除点
    cloud->erase(index);//删除第一个
    index = cloud->begin() + 5;
    cloud->erase(cloud->begin());//删除第5个
    pcl::PointXYZ point = { 1, 1, 1 };
    // 添加点
    //在索引号为5的位置1上插入一点,原来的点后移一位
    cloud->insert(cloud->begin() + 5, point);
    cloud->push_back(point);//从点云最后面插入一点
    std::cout << cloud->points[5].x;//输出1
}

3. 新建一个点云对象,然后对点云对象进行初始化,并随机赋值

  • 初始化点云
pcl::PointCloud<pcl::PointXYZ> cloud;
// 创建点云
cloud.width=5;
cloud.height=1;
cloud.is_dense=false;
cloud.points.resize(cloud.width*cloud.height);
// 随机赋值
for(size_t i=0;i<cloud.points.size();++i)
{
    cloud.points[i].x=1024*rand()/(RAND_MAX+1.0f);
    cloud.points[i].y=1024*rand()/(RAND_MAX+1.0f);
    cloud.points[i].z=1024*rand()/(RAND_MAX+1.0f);
}
// 将点云保存为pcd文件
pcl::io::savePCDFileASCII("test_pcd.pcd",cloud);

4. 点云可视化基本操作

  • PCLVisualizer
// 定义一个显示器
pcl::visualization::PCLVisualizer viewer("registration Viewer"); 
// 给点云定义一个颜色 RGB模式  为全G 绿色
pcl::visualization::PointCloudColorHandlerCustom<pcl::PointXYZ> src_h(cloud, 0, 200, 200);
// 设置背景颜色  255,255,255就是白色
viewer.setBackgroundColor(255, 255, 255);  
// 把点云加入到显示器里
viewer.addPointCloud(cloud, "source cloud");  
  • 点云视窗类:CloudViewer
// 创建viewer对象
pcl::visualization::CloudViewer viewer("Cloud Viewer");     
// 加载点云
viewer.showCloud(cloud);

5. 点的顺时针、逆时针排序

  • 顺时针
//若点a大于点b,即点a在点b顺时针方向,返回true,否则返回false
bool PointCmp(const pcl::PointXYZ a, const pcl::PointXYZ b, const pcl::PointXYZ center)
{
    if (a.x >= 0 && b.x < 0)
        return true;
    if (a.x == 0 && b.x == 0)
        return a.y > b.y;
    //向量OA和向量OB的叉积
    float det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y);
    if (det < 0)
        return true;
    if (det > 0)
        return false;
    //向量OA和向量OB共线,以距离判断大小
    float d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y);
    float d2 = (b.x - center.x) * (b.x - center.y) + (b.y - center.y) * (b.y - center.y);
    return d1 > d2;
}
//点集合
void sortClockwise(pcl::PointXYZ vPoints[], int n)
{
    //计算重心
    pcl::PointXYZ center;
    double x = 0, y = 0, z = 0;
    for (int i = 0; i < n; i++)
    {
        x += vPoints[i].x;
        y += vPoints[i].y;
        z += vPoints[i].z;
    }
    center.x = x / n;
    center.y = y / n;
    center.z = z / n;

    cout << "center:" << center.x << "," << center.y << "," << center.z << endl;

    //冒泡排序
    for (int i = 0; i < n - 1; i++)
    {
        for (int j = 0; j < n - i - 1; j++)
        {
            if (PointCmp(vPoints[j], vPoints[j + 1], center))
                std::swap(vPoints[j], vPoints[j + 1]);
        }
    }

    //return vPoints;
}
  • 逆时针、顺时针
#include <pcl/io/pcd_io.h>
#include <pcl/point_types.h>
#include <pcl/common/centroid.h>
#include <pcl/visualization/pcl_visualizer.h>
#include <pcl/geometry/planar_polygon.h>//定义多边形

// 逆时针排序
void CounterClockWiseSortPoints(pcl::PointCloud<pcl::PointXYZ>& vPoints)
{
    int cnt = static_cast<int>(vPoints.size());
    if (cnt < 3)
        return;
    //计算中心
    Eigen::Vector4f centroid;					// 质心
    pcl::compute3DCentroid(vPoints, centroid);	// 齐次坐标,(c0,c1,c2,1)
    pcl::PointXYZ center{ centroid[0],centroid[1] ,centroid[2] };
    //若点a小于点b,即点a在点b逆时针方向,返回true,否则返回false
    auto PointCmp = [](const pcl::PointXYZ& a, const pcl::PointXYZ& b, const pcl::PointXYZ& center)
    {
        if (a.x <= 0 && b.x > 0)
            return true;
        if (a.x == 0 && b.x == 0)
            return a.y < b.y;
        //向量OA和向量OB的叉积,向量OA和OB的叉积大于0,则向量OA在向量OB的逆时针方向,即点A小于点B。
        float det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y);
        if (det < 0)
            return false;
        if (det > 0)
            return true;
        //向量OA和向量OB共线,以距离判断大小
        float d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y);
        float d2 = (b.x - center.x) * (b.x - center.y) + (b.y - center.y) * (b.y - center.y);
        return d1 > d2;
    };

    //冒泡排序
    for (int i = 0; i < cnt - 1; i++)
    {
        for (int j = 0; j < cnt - i - 1; j++)
        {
            if (PointCmp(vPoints[j], vPoints[j + 1], center))
                std::swap(vPoints[j], vPoints[j + 1]);
        }
    }
}

// 顺时针排序
void ClockWiseSortPoints(pcl::PointCloud<pcl::PointXYZ>& vPoints)
{
    int cnt = static_cast<int>(vPoints.size());
    if (cnt < 3)
        return;
    //计算中心
    Eigen::Vector4f centroid;					// 质心
    pcl::compute3DCentroid(vPoints, centroid);	// 齐次坐标,(c0,c1,c2,1)
    pcl::PointXYZ center{ centroid[0],centroid[1] ,centroid[2] };
    //若点a大于点b,即点a在点b顺时针方向,返回true,否则返回false
    auto PointCmp = [](const pcl::PointXYZ& a, const pcl::PointXYZ& b, const pcl::PointXYZ& center)
    {
        if (a.x >= 0 && b.x < 0)
            return true;
        if (a.x == 0 && b.x == 0)
            return a.y > b.y;
        //向量OA和向量OB的叉积,向量OA和OB的叉积大于0,则向量OA在向量OB的逆时针方向,即点A小于点B。
        float det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y);
        if (det < 0)
            return false;
        if (det > 0)
            return true;
        //向量OA和向量OB共线,以距离判断大小
        float d1 = (a.x - center.x) * (a.x - center.x) + (a.y - center.y) * (a.y - center.y);
        float d2 = (b.x - center.x) * (b.x - center.y) + (b.y - center.y) * (b.y - center.y);
        return d1 > d2;
    };

    //冒泡排序
    for (int i = 0; i < cnt - 1; i++)
    {
        for (int j = 0; j < cnt - i - 1; j++)
        {
            if (PointCmp(vPoints[j], vPoints[j + 1], center))
                std::swap(vPoints[j], vPoints[j + 1]);
        }
    }
}

int main(int argc, char** argv)
{
    //--------------------------加载点云数据----------------------------
    pcl::PointCloud<pcl::PointXYZ>::Ptr cloud(new pcl::PointCloud<pcl::PointXYZ>);

    pcl::PCDReader reader;
    reader.read("排序.pcd", *cloud);
    std::cerr << "原始点云点的个数: " << cloud->points.size() << std::endl;
    ClockWiseSortPoints(*cloud);
    //CounterClockWiseSortPoints(*cloud); // 逆时针排序
    //增加多边形
    pcl::PlanarPolygon<pcl::PointXYZ> polygon;
    pcl::PointCloud<pcl::PointXYZ> contour;
    contour.width = cloud->width;
    contour.height = 1;
    contour.is_dense = false;
    contour.resize(contour.height * contour.width);

    for (size_t i = 0; i < cloud->points.size(); ++i)
    {
        cout << "第" << i << "个点的坐标为" << cloud->points[i] << endl;
        contour.points[i] = cloud->points[i];
    }

    polygon.setContour(contour);
    pcl::io::savePCDFileASCII("顺时针排列.pcd", *cloud);
    pcl::visualization::PCLVisualizer viewer("Viewer");
    viewer.setWindowName("平面点云边界点排序");
    viewer.addPolygon(polygon, 255, 0, 0, "ploygon", 0);
    viewer.addPointCloud<pcl::PointXYZ>(cloud, "sample cloud");
    viewer.setPointCloudRenderingProperties(pcl::visualization::PCL_VISUALIZER_POINT_SIZE, 5, "sample cloud"); // 设置点云大
    while (!viewer.wasStopped())
    {
        viewer.spinOnce(1);
    }
    return 0;
}

6. 点云合并

// Objects for storing the point clouds.
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloudA(new pcl::PointCloud<pcl::PointXYZRGBA>);
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloudB(new pcl::PointCloud<pcl::PointXYZRGBA>);
pcl::PointCloud<pcl::PointXYZRGBA>::Ptr cloudC(new pcl::PointCloud<pcl::PointXYZRGBA>);

// 读取两个点云文件
if (pcl::io::loadPLYFile<pcl::PointXYZRGBA>("C:\\Users\\fhlhc\\Desktop\\left.ply", *cloudA) != 0)
{
    return -1;
}
if (pcl::io::loadPLYFile<pcl::PointXYZRGBA>("C:\\Users\\fhlhc\\Desktop\\right.ply", *cloudB) != 0)
{
    return -1;
}

// Create cloud "C", with the points of both "A" and "B".点云A、B合并生成C
*cloudC = (*cloudA) + (*cloudB);
// Now cloudC->points.size() equals cloudA->points.size() + cloudB->points.size().

//可视化
pcl::visualization::PCLVisualizer viewer("合并两个点云");

7. 通过某点对空间直线做垂线,求垂足

pcl::PointXYZ GetFootOfPerpendicular(
    const pcl::PointXYZ pt,     // 直线外一点
    const pcl::PointXYZ begin,  // 直线开始点
    const pcl::PointXYZ end)   // 直线结束点
{
    pcl::PointXYZ retVal;

    double dx = begin.x - end.x;
    double dy = begin.y - end.y;
    double dz = begin.z - end.z;

    if (abs(dx) < 0.00000001 && abs(dy) < 0.00000001 && abs(dz) < 0.00000001)
    {
        retVal = begin;
        return retVal;
    }

    double u = (pt.x - begin.x) * (begin.x - end.x) + (pt.y - begin.y) * (begin.y - end.y) + (pt.z - begin.z) * (begin.z - end.z);
    u = u / ((dx * dx) + (dy * dy) + (dz * dz));

    retVal.x = begin.x + u * dx;
    retVal.y = begin.y + u * dy;
    retVal.z = begin.z + u * dz;

    return retVal;
}